/* Copyright (c) 2020 XEPIC Corporation Limited */
/*
 * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include "ivl_alloc.h"
#include "vvp_priv.h"

int number_is_unknown(ivl_expr_t expr) {
  const char *bits;
  unsigned idx;

  if (ivl_expr_type(expr) == IVL_EX_ULONG) return 0;

  assert(ivl_expr_type(expr) == IVL_EX_NUMBER);

  bits = ivl_expr_bits(expr);
  for (idx = 0; idx < ivl_expr_width(expr); idx += 1)
    if ((bits[idx] != '0') && (bits[idx] != '1')) return 1;

  return 0;
}

/*
 * This function returns TRUE if the number can be represented as a
 * lim_wid immediate value. This amounts to verifying that any upper
 * bits are the same. For a negative value we do not support the most
 * negative twos-complement value since it can not be negated. This
 * code generator always emits positive values, hence the negation
 * requirement.
 */
int number_is_immediate(ivl_expr_t expr, unsigned lim_wid,
                        int negative_ok_flag) {
  const char *bits;
  unsigned nbits = ivl_expr_width(expr);
  char pad_bit = '0';
  unsigned idx;

  /* We can only convert numbers to an immediate value. */
  if (ivl_expr_type(expr) != IVL_EX_NUMBER &&
      ivl_expr_type(expr) != IVL_EX_ULONG &&
      ivl_expr_type(expr) != IVL_EX_DELAY)
    return 0;

  /* If a negative value is OK, then we really have one less
   * significant bit because of the sign bit. */
  if (negative_ok_flag) lim_wid -= 1;

  /* This is an unsigned value so it can not have the -2**N problem. */
  if (ivl_expr_type(expr) == IVL_EX_ULONG) {
    unsigned long imm;
    if (lim_wid >= 8 * sizeof(unsigned long)) return 1;
    /* At this point we know that lim_wid is smaller than an
     * unsigned long variable. */
    imm = ivl_expr_uvalue(expr);
    if (imm < (1UL << lim_wid))
      return 1;
    else
      return 0;
  }

  /* This is an unsigned value so it can not have the -2**N problem. */
  if (ivl_expr_type(expr) == IVL_EX_DELAY) {
    uint64_t imm;
    if (lim_wid >= 8 * sizeof(uint64_t)) return 1;
    /* At this point we know that lim_wid is smaller than a
     * uint64_t variable. */
    imm = ivl_expr_delay_val(expr);
    if (imm < ((uint64_t)1 << lim_wid))
      return 1;
    else
      return 0;
  }

  bits = ivl_expr_bits(expr);

  if (ivl_expr_signed(expr) && bits[nbits - 1] == '1') pad_bit = '1';

  if (pad_bit == '1' && !negative_ok_flag) return 0;

  for (idx = lim_wid; idx < nbits; idx += 1)
    if (bits[idx] != pad_bit) return 0;

  /* If we have a negative number make sure it is not too big. */
  if (pad_bit == '1') {
    for (idx = 0; idx < lim_wid; idx += 1)
      if (bits[idx] == '1') return 1;
    return 0;
  }

  return 1;
}

/*
 * We can return positive or negative values. You must verify that the
 * number is not unknown (number_is_unknown) and is small enough
 * (number_is_immediate).
 */
long get_number_immediate(ivl_expr_t expr) {
  long imm = 0;

  switch (ivl_expr_type(expr)) {
    case IVL_EX_ULONG:
      imm = ivl_expr_uvalue(expr);
      break;

    case IVL_EX_NUMBER: {
      const char *bits = ivl_expr_bits(expr);
      unsigned nbits = ivl_expr_width(expr);
      unsigned idx;
      /* We can not copy more bits than fit into a long. */
      if (nbits > 8 * sizeof(long)) nbits = 8 * sizeof(long);
      for (idx = 0; idx < nbits; idx += 1) switch (bits[idx]) {
          case '0':
            break;
          case '1':
            imm |= 1L << idx;
            break;
          default:
            assert(0);
        }
      if (ivl_expr_signed(expr) && bits[nbits - 1] == '1' &&
          nbits < 8 * sizeof(long))
        imm |= -1UL << nbits;
      break;
    }

    default:
      assert(0);
  }

  return imm;
}

uint64_t get_number_immediate64(ivl_expr_t expr) {
  uint64_t imm = 0;

  switch (ivl_expr_type(expr)) {
    case IVL_EX_ULONG:
      imm = ivl_expr_uvalue(expr);
      break;

    case IVL_EX_NUMBER: {
      const char *bits = ivl_expr_bits(expr);
      unsigned nbits = ivl_expr_width(expr);
      unsigned idx;
      for (idx = 0; idx < nbits; idx += 1) switch (bits[idx]) {
          case '0':
            break;
          case '1':
            assert(idx < 64);
            imm |= UINT64_C(1) << idx;
            break;
          default:
            assert(0);
        }
      if (ivl_expr_signed(expr) && bits[nbits - 1] == '1' && nbits < 64)
        imm |= (-UINT64_C(1)) << nbits;
      break;
    }

    default:
      assert(0);
  }

  return imm;
}

void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) {
  switch (ivl_expr_type(expr)) {
    case IVL_EX_NUMBER:
    case IVL_EX_ULONG: {
      assert(number_is_immediate(expr, IMM_WID, 1));
      if (number_is_unknown(expr)) {
        /* We are loading a 'bx so mimic %ix/get. */
        fprintf(vvp_out, "    %%ix/load %u, 0, 0;\n", ix);
        fprintf(vvp_out, "    %%flag_set/imm 4, 1;\n");
        break;
      }
      long imm = get_number_immediate(expr);
      if (imm >= 0) {
        fprintf(vvp_out, "    %%ix/load %u, %ld, 0;\n", ix, imm);
      } else {
        fprintf(vvp_out, "    %%ix/load %u, 0, 0; loading %ld\n", ix, imm);
        fprintf(vvp_out, "    %%ix/sub %u, %ld, 0;\n", ix, -imm);
      }
      /* This can not have have a X/Z value so clear flag 4. */
      fprintf(vvp_out, "    %%flag_set/imm 4, 0;\n");
    } break;

      /* Special case: There is an %ix instruction for
         reading index values directly from variables. In
         this case, try to use that special instruction. */
    case IVL_EX_SIGNAL: {
      ivl_signal_t sig = ivl_expr_signal(expr);

      unsigned word = 0;
      if (ivl_signal_dimensions(sig) > 0) {
        ivl_expr_t ixe;
        const char *type = ivl_expr_signed(expr) ? "/s" : "";

        /* Detect the special case that this is a
           variable array. In this case, the ix/getv
           will not work, so do it the hard way. */
        if (ivl_signal_type(sig) == IVL_SIT_REG) {
          draw_eval_vec4(expr);
          fprintf(vvp_out, "    %%ix/vec4%s %u;\n", type, ix);
          break;
        }

        ixe = ivl_expr_oper1(expr);
        if (number_is_immediate(ixe, IMM_WID, 0)) {
          assert(!number_is_unknown(ixe));
          word = get_number_immediate(ixe);
        } else {
          draw_eval_vec4(expr);
          fprintf(vvp_out, "    %%ix/vec4%s %u;\n", type, ix);
          break;
        }
      }
      const char *type = ivl_signal_signed(sig) ? "/s" : "";
      fprintf(vvp_out, "    %%ix/getv%s %u, v%p_%u;\n", type, ix, sig, word);
      break;
    }

    default:
      draw_eval_vec4(expr);
      /* Is this a signed expression? */
      if (ivl_expr_signed(expr)) {
        fprintf(vvp_out, "    %%ix/vec4/s %u;\n", ix);
      } else {
        fprintf(vvp_out, "    %%ix/vec4 %u;\n", ix);
      }
      break;
  }
}

/*
 * This function, in addition to setting the value into index 0, sets
 * bit 4 to 1 if the value is unknown.
 */
void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix) {
  switch (ivl_expr_value(expr)) {
    case IVL_VT_BOOL:
    case IVL_VT_LOGIC:
      eval_logic_into_integer(expr, ix);
      break;

    case IVL_VT_REAL:
      draw_eval_real(expr);
      fprintf(vvp_out, "    %%cvt/sr %u;\n", ix);
      break;

    default:
      fprintf(stderr, "XXXX ivl_expr_value == %d\n", ivl_expr_value(expr));
      assert(0);
  }
}

char *process_octal_codes(const char *in, unsigned width) {
  unsigned idx = 0;
  unsigned ridx = 0;
  unsigned str_len = strlen(in);
  char *out = (char *)malloc(str_len + 1);

  /* If we do not have any octal codes just return the input. */
  if (width / 8 == str_len) {
    strcpy(out, in);
    return out;
  }

  while (ridx < str_len) {
    /* An octal constant always has three digits. */
    if (in[ridx] == '\\') {
      out[idx] = (in[ridx + 1] - '0') * 64 + (in[ridx + 2] - '0') * 8 +
                 (in[ridx + 3] - '0');
      idx += 1;
      ridx += 4;
    } else {
      out[idx] = in[ridx];
      idx += 1;
      ridx += 1;
    }
  }
  out[idx] = '\0';

  return out;
}
